package com.wn.ticket.security.backstage;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.wn.ticket.common.JwtUtils;
import com.wn.ticket.common.ResponseEntity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

import java.util.Arrays;

/**
 * @author wuqingting
 * @date 2022/8/22
 */
@Configuration
@Order(2)
@EnableGlobalMethodSecurity(prePostEnabled = true)//启用注解控制权限的方式
public class BackstageSecurityConfiguration extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //添加过滤器时,应注意顺序,用户登录验证需要在jwt验证前(用户登录验证成功就不会使用filterChain.doFilter,不会再到jwt过滤器),否则/api/login请求也会经过jwt验证过滤器
        http.addFilterBefore(backstageJwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);//配置jwt验证过滤器
        http.addFilterBefore(backstageAuthenticationFilter(), BackstageJwtAuthenticationFilter.class);//配置登录验证过滤器
        http.cors();//跨域配置
        http.antMatcher("/backstage/**")//指定以/api/**开头的使用以下配置
                .authorizeHttpRequests()
                .antMatchers(HttpMethod.OPTIONS).permitAll()//OPTIONS方法直接放行
                .antMatchers("/backstage/admin/login","/backstage/captcha").permitAll()//放行
                .anyRequest().authenticated()//其他全部需要认证
                .and()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)//指定session生成策略,Spring Security不会创建HttpSession，也不会使用它来获取SecurityContext
                .and().csrf().disable();
        http.exceptionHandling().accessDeniedHandler((req,resp,e)->{//无访问权限异常(由于GlobalHandler类中对web包下所有异常进行了拦截,所以此处不能失效,除非移除GlobalHandler类(本项目已移除))
            String s = new ObjectMapper().writeValueAsString(new ResponseEntity<>("402","权限不足",null));
            resp.setContentType("application/json;charset=UTF-8");
            resp.getWriter().write(s);
            resp.getWriter().close();
        });
    }

    @Autowired
    private AdminUserDetailsService adminUserDetailsService;
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //super.configure(auth);
        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();//密码编码器
        auth.userDetailsService(adminUserDetailsService).passwordEncoder(encoder);//指定userdetailService和密码编码器
    }

    @Autowired
    private JwtUtils jwtUtil;
    //@Bean//配置登录验证过滤器,并处理登录结果
    public BackstageAuthenticationFilter backstageAuthenticationFilter() throws Exception {
        BackstageAuthenticationFilter apiAuthenticationFilter = new BackstageAuthenticationFilter("/backstage/admin/login");//指定过滤器处理的路径
        apiAuthenticationFilter.setAuthenticationManager(authenticationManager());//设置身份验证管理器
        apiAuthenticationFilter.setAuthenticationSuccessHandler(((request, response, authentication) -> {//验证成功处理器
            AdminUser oaUser = (AdminUser) authentication.getPrincipal();//从认证结果中拿到已验证的用户
            String token = jwtUtil.createJWT(oaUser.getId(), oaUser.getUsername());//创建token
            String s = new ObjectMapper().writeValueAsString(new ResponseEntity<>("200", "ok", token));
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().write(s);
            response.getWriter().close();
        }));
        apiAuthenticationFilter.setAuthenticationFailureHandler(((request, response, exception) -> {//验证失败处理器
            String s = new ObjectMapper().writeValueAsString(new ResponseEntity<>("400", "用户名或密码错误", null));
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().write(s);
            response.getWriter().close();
        }));

        return apiAuthenticationFilter;
    }

    @Bean
    public CorsFilter corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();

        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.addAllowedOriginPattern("*");
        corsConfiguration.addAllowedHeader("*");
        corsConfiguration.addAllowedMethod("*");
        corsConfiguration.setAllowCredentials(true);
        source.registerCorsConfiguration("/**", corsConfiguration);
        return new CorsFilter(source);
    }

    /*@Bean//用户密码的加密方式,不需要在configure中显示指定即可生效
    public PasswordEncoder passwordEncoder() {
        //return NoOpPasswordEncoder.getInstance();
        return new BCryptPasswordEncoder(4);//加密4次
    }
    @Bean//对jwt验证失败抛出的各种异常进行处理
    public AuthenticationFailureHandler authenticationFailureHandler() {
        return (req, resp, exception) -> {
            ObjectMapper objectMapper = new ObjectMapper();
            String s = objectMapper.writeValueAsString(new ResponseEntity<>("400",  exception.getMessage(),null));
            resp.setContentType("application/json;charset=UTF-8");
            resp.getWriter().write(s);
            resp.getWriter().close();
        };
    }*/

    @Autowired
    private AdminUserDetailsService oaUserDetailsService;
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private AuthenticationFailureHandler authenticationFailureHandler;
    //@Bean//jwt验证的过滤器
    public BackstageJwtAuthenticationFilter backstageJwtAuthenticationFilter() {
        return new BackstageJwtAuthenticationFilter(jwtUtil,authenticationFailureHandler,oaUserDetailsService,redisTemplate);
    }
}
